#include "stdafx.h"
#include "Board.h"
#include <ctype.h>
#include <time.h>
#include <math.h>
#include <string.h>
#include <iostream>

void Board::initialisePattern()
{
	//load pattern into storage buffer
	pattern.push_back(Point(0,0));
	pattern.push_back(Point(3,0));
	pattern.push_back(Point(6,0));
	pattern.push_back(Point(9,0));
	pattern.push_back(Point(1,1));
	pattern.push_back(Point(4,1));
	pattern.push_back(Point(5,1));
	pattern.push_back(Point(8,1));
	pattern.push_back(Point(2,2));
	pattern.push_back(Point(3,2));
	pattern.push_back(Point(4,2));
	pattern.push_back(Point(5,2));
	pattern.push_back(Point(6,2));
	pattern.push_back(Point(7,2));
	pattern.push_back(Point(0,3));
	pattern.push_back(Point(2,3));
	pattern.push_back(Point(3,3));
	pattern.push_back(Point(6,3));
	pattern.push_back(Point(7,3));
	pattern.push_back(Point(9,3));
	pattern.push_back(Point(1,4));
	pattern.push_back(Point(2,4));
	pattern.push_back(Point(4,4));
	pattern.push_back(Point(5,4));
	pattern.push_back(Point(7,4));
	pattern.push_back(Point(8,4));
	pattern.push_back(Point(1,5));
	pattern.push_back(Point(2,5));
	pattern.push_back(Point(4,5));
	pattern.push_back(Point(5,5));
	pattern.push_back(Point(7,5));
	pattern.push_back(Point(8,5));
	pattern.push_back(Point(0,6));
	pattern.push_back(Point(2,6));
	pattern.push_back(Point(3,6));
	pattern.push_back(Point(6,6));
	pattern.push_back(Point(7,6));
	pattern.push_back(Point(9,6));
	pattern.push_back(Point(2,7));
	pattern.push_back(Point(3,7));
	pattern.push_back(Point(4,7));
	pattern.push_back(Point(5,7));
	pattern.push_back(Point(6,7));
	pattern.push_back(Point(7,7));
	pattern.push_back(Point(1,8));
	pattern.push_back(Point(4,8));
	pattern.push_back(Point(5,8));
	pattern.push_back(Point(8,8));
	pattern.push_back(Point(0,9));
	pattern.push_back(Point(3,9));
	pattern.push_back(Point(6,9));
	pattern.push_back(Point(9,9));
}

Board::Board()
{
	//std::cout << "CREATING NEW BOARD" << std::endl;
	for ( int i=0; i < getBoardSize(); i++)
	{
		for ( int j=0; j < getBoardSize(); j++ )
		{
			board[i][j] = '-';
		}
	}

	lastSearch.x = 0;
	lastSearch.y = 0;
	moves = 0;
	isLineSearchVertical = true;
	lineSearch.x = 1;
	lineSearch.y = -1;
	initialisePattern();
}

char Board::getBoardValue(int x, int y)
{
	return board[y][x];
}

int Board::getBoardSize()
{
	return 10;
}

void Board::setBoardValue(int x, int y, char val)
{
	std::cout << x << " " << y << std::endl;
	board[y][x] = val;
}

void Board::printBoard()
{
	// Print colum headings
	std::cout << "  ";
	for ( int i = 0; i < getBoardSize() ; i++ )
	{
		std::cout << i << " ";
	}
	std::cout << std::endl;

	// print board rows
	for ( int i = 0; i < getBoardSize() ; i++)
	{
		std::cout << i << " ";
		for ( int j = 0; j < getBoardSize(); j++)
		{
			std::cout << board[i][j] << " ";
		}
		std::cout << std:: endl;
	}
}

int Board::placeShip(int sx, int sy, int ex, int ey)
{
	bool horizontal = false;
	bool vertical = false;
	
	int difx = sx - ex;
	int dify = sy - ey;

	// detect alignment of ship to be deployed
	if ( sx == ex ) vertical = true;
	if ( sy == ey ) horizontal = true;
	if ( abs(dify) == abs(difx) ){horizontal = true; vertical = true;}

	if ( horizontal || vertical )
	{
		board[sy][sx] = '1';
		
		// deploy digonal ship
		if ( horizontal && vertical )
		{
			if ( sx < ex && sy < ey )
			{
				while ( sx < ex && sy < ey )
				{
					sx++;
					sy++;
					board[sy][sx] = '1';
				}
			}
			else if ( sx < ex && sy > ey )
			{
				while ( sx < ex && sy > ey )
				{
					sx++;
					sy--;
					board[sy][sx] = '1';
				}
			}
			else if ( sx > ex && sy > ey )
			{
				while ( sx > ex && sy > ey )
				{
					sx--;
					sy--;
					board[sy][sx] = '1';
				}
			}
			else if ( sx > ex && sy < ey )
			{
				while ( sx > ex && sy < ey )
				{
					sx--;
					sy++;
					board[sy][sx] = '1';
				}
			}
			return 0;
		}

		//deploy horizontal ship
		if ( horizontal )
		{
			if ( sx < ex )
			{
				while ( sx < ex )
				{
					sx++;
					board[sy][sx] = '1';
				}
			}
			else if ( sx > ex )
			{
				while ( sx > ex )
				{
					sx--;
					board[sy][sx] = '1';
				}
			}
			return 0;
		}

		//deply vertical ship
		if ( vertical )
		{
			if ( sy < ey )
			{
				while ( sy < ey )
				{
					sy++;
					board[sy][sx] = '1';
				}
			}
			else if ( sy > ey )
			{
				while ( sy > ey )
				{
					sy--;
					board[sy][sx] = '1';
				}
			}
			return 0;
		}
			
	}
	return 0;
}

// test if a ship will overlap with another when deployed
bool Board::clearPath(int x, int y, int dir)
{
	// 0 = vertical, 1 = horizontal, 2 = top right diagonal, 3 = bottom right diagonal, 4 = bottom left diagonal, 5 = top left diagonal, 6 = -vertical, 7 =-horizontal
	switch (dir)
	{
	case down :
		if ( (y + 2) > 9 ) return false;
		for ( int i = 0; i < 3; i ++)
		{
			if ( board[y+i][x] == '1' ) return false;
		}
		break;
	case right :
		if ( (x + 2) > 9 ) return false;
		for ( int i = 0; i < 3; i ++)
		{
			if ( board[y][x+i] == '1' ) return false;
		}
		break;
	case upRight :
		if ( (y - 2) < 0 || (x + 2) > 9 ) return false;
		for ( int i = 0; i < 3; i ++)
		{
			if ( board[y-i][x+i] == '1' ) return false;
		}
		break;
	case downRight :
		if ( (y + 2) > 9 || (x + 2) > 9  ) return false;
		for ( int i = 0; i < 3; i ++)
		{
			if ( board[y+i][x+i] == '1' ) return false;
		}
		break;
	case downLeft :
		if ( (y + 2) > 9 || (x - 2) < 0 ) return false;
		for ( int i = 0; i < 3; i ++)
		{
			if ( board[y+i][x-i] == '1' ) return false;
		}
		break;
	case upLeft :
		if ( (y - 2) < 0 || (x - 2) < 0 ) return false;
		for ( int i = 0; i < 3; i ++)
		{
			if ( board[y-i][x-i] == '1' ) return false;
		}
		break;
	case up :
		if ( (y - 2) < 0 ) return false;
		for ( int i = 0; i < 3; i ++)
		{
			if ( board[y-i][x] == '1' ) return false;
		}
		break;
	case left :
		if ( (x - 2) < 0 ) return false;
		for ( int i = 0; i < 3; i ++)
		{
			if ( board[y][x-i] == '1' ) return false;
		}
	}
	return true;
}

// test to ensure that ship start cords are not imdediatly next to another ship
bool Board::isNeibour(int x, int y)
{
	if ( board[x][y] == '1' ) return true;
	if ( x < 9 )
	{
		if ( board[x+1][y] == '1' ) return true;
		if ( y < 10 )
		{
			if ( board[x+1][y+1] == '1' ) return true;
		}
		if ( y > 0 )
		{
			if ( board[x+1][y-1] == '1' ) return true;
		}
	}
	if ( y > 0 )
	{
		if ( board[x][y-1] == '1' ) return true;
	}
	if ( y < 9 )
	{
		if ( board[x][y+1] == '1' ) return true;
	}
	if ( x > 0 )
	{
		if ( board[x-1][y] == '1' ) return true;
		if ( y < 9 )
		{
			if ( board[x-1][y+1] == '1' ) return true;
		}
		if ( y > 0 )
		{
			if ( board[x-1][y-1] == '1' ) return true;
		}
	}

	return false;
}

// deploy ships to a board randomly
void Board::populate()
{
	// seed number generator
	srand(time(NULL));

	int startx = 0;
	int starty = 0;
	int dir = 0;
	
	//polulate board
	for ( int i = 0; i < 4 ; i++)
	{
		 startx = rand()%10;
		 starty = rand()%10;
		 dir = rand()%6;
		 while ( isNeibour(startx, starty) || !clearPath(startx,starty,dir) )
		 {
			startx = rand()%10;
			starty = rand()%10;
			dir = rand()%8;
		 }
		 switch( dir )
		 {
		 case down :
			placeShip(startx, starty, startx, starty+2);
			break;
		 case right :
			placeShip(startx, starty, startx+2, starty);
			break;
		 case upRight :
			placeShip(startx, starty, startx+2, starty-2);
			break; 
		 case downRight :
			placeShip(startx, starty, startx+2, starty+2);
			break;
		 case downLeft :
			placeShip(startx, starty, startx-2, starty+2);
			break;
		 case upLeft :
			placeShip(startx, starty, startx-2, starty-2);
			break;
		 case up :
			placeShip(startx, starty, startx, starty-2);
			break;
		 case left :
			placeShip(startx, starty, startx-2, starty);
		 }
	}
}

// scan board for remaining ships
bool Board::shipsLeft()
{
	for ( int i = 0; i < getBoardSize() ; i++)
	{
		for ( int j = 0; j < getBoardSize() ; j++ )
		{
			if (board[i][j] == '1') return true;
		}
	}
	return false;
}

// test to see if a hit was made
bool Board::hitMiss(int x, int y)
{
	if ( board[y][x] == '1' )
	{
		board[y][x] = 'x';
		return true;
	}
	return false;
}

// store a hit
void Board::setHit(int ox, int oy)
{
	Point p;
	p.x= ox;
	p.y= oy;
	hits.push_back(p);
}

// test to see if a cord has already been guessed
bool Board::alreadyGuessed(int x, int y)
{
	if ( board[y][x] != '-' ) return true;
	return false;
}

Point Board::getRandomPatternHit()
{
	// storage pointer
	Point p;
	do 
	{
		// seed random generator
		srand(time(NULL));
		// genearte location in pattern buffer
		int extract = rand()%pattern.size();
		// extract coords from pattern buffer
		p = pattern[extract];
		// remove used coords from pattern buffer
		pattern.erase(pattern.begin()+extract);
	}
	while ( alreadyGuessed(p.x,p.y) ); // test location hasnt alreay been guessed
	// return coords
	return p;
}

Point Board::getLineHit()
{
	do
	{
		if ( isLineSearchVertical )
		{
			if ( lineSearch.y < 9 )
			{
				++lineSearch.y;
				//return lineSearch;
			}
			else
			{
				lineSearch.x+=3;
				lineSearch.y = 0;
				// check we havent run off the end of the row
				if ( lineSearch.x > 9 )
				{
					isLineSearchVertical = false;
					lineSearch.x = -1;
					lineSearch.y = 1;
				}
				else
				{
					//return lineSearch;
				}
			}
		}
		if ( !isLineSearchVertical )
		{
			if ( lineSearch.x < 10 )
			{
				++lineSearch.x;
				//return lineSearch;
			}
			else
			{
				lineSearch.y+=3;
				lineSearch.x=0;
				// check we havent run off the end of the column
				if (lineSearch.y > 9 )
				{
					lineSearch.x = 0;
					lineSearch.y = 0;
				}
				else
				{
					//return lineSearch;
				}
			}
		}
	}
	while ( alreadyGuessed(lineSearch.x,lineSearch.y) );
	return lineSearch;
}

// Search board in checker board style till a hit is found. Reduces search space by half
Point Board::getCheckBoardHit()
{
	
	do
	{
		std::cout << "Number of moves = " << moves << std::endl;
		// check to see if the search is in the start position
		if ( lastSearch.x == 0 && lastSearch.y == 0 && moves == 0)
		{
			std::cout << "Last Search x = " << lastSearch.x << std::endl;
			// Randomly chose which square of the checkerboard to start on
			srand(time(NULL));
			int x = rand()%2;
			// incremnt x position ocordingly
			if ( x == 1 )
			{
				++lastSearch.x;
			}
			else
			{
				moves++;
			}
			return lastSearch;
		}
		else
		{
			// increment the search coordinates 
			lastSearch.x += 2;
			// test the row hasnt been overrun 
			if ( lastSearch.x > 9 )
			{
				// add on either 0 or 1 depending on which row of the checkerboard we are on
				lastSearch.x = 0 + moves%2;
				++lastSearch.y;
				++moves;
				//std::cout << "Last Search x = " << lastSearch.x << std::endl;
				return lastSearch;
			}
			// test the colum hasnt been overrun 
			if ( lastSearch.y > 9 )
			{
				lastSearch.y = 9;
				++moves;
				//std::cout << "Last Search x = " << lastSearch.x << std::endl;
				return lastSearch;
			}
		}
	}
	while (alreadyGuessed(lastSearch.x,lastSearch.y));
	//std::cout << "RETURNING lastSearch = " << lastSearch.x << " " << lastSearch.y << std::endl;
	return lastSearch;
}

// generated random cords for oponent to guess with
Point Board::getRandomHit()
{
	Point p;
	int x = 0;
	int y = 0;
	//int retVal[2];
	x = rand()%10;
	y = rand()%10;
	while ( alreadyGuessed(x,y) )
	{
		x = rand()%10;
		y = rand()%10;
	}
	p.x = x;
	p.y = y;
	return p;
}

// get the num of his currently stored
int Board::getNumHits()
{
	return hits.size();
}

// make a guess for the computer oponent
// if a ship already hit search arround it else make a random guess
Point Board::getHit()
{
	int split = 0;
	int i = 0;
	int x = 0;
	int y = 0;
	Point p;
	srand(time(NULL));
	split = rand()%10;
	std::cout << "GOT HERE" << std::endl;
	if ( split < 8 )
	{
		std::cout << "PROCESSING HIT" << std::endl;
		i = rand()%hits.size();
		Point cords;
		cords = hits[i];
		x = cords.x;
		y = cords.y;
		bool guessed = false;
		for ( int i = -1; i < 2 ; i++ )
		{
			if ( (y+i) < 0 || (y+i) > 9) continue;
			for ( int j = -1; j < 2 ; j++ )
			{
				if ( (x+j) < 0 || (x+j) > 9 ) continue;
				if ( !alreadyGuessed(x+i,y+j) )
				{
					p.x = (x+i);
					p.y = (y+j);
					guessed = true;
					break;
				}	
			}
			if ( guessed ) break;
		}
		if ( !guessed )
		{
			//getRandomHit();
			//std::cout << "Running checkerboard search" << std::endl;
			p = getRandomPatternHit();
			Point temp;
			// debug print hits code
			/*std::cout << "PRINTING HITS" << std::cout;

			for ( int i = 0; i < hits.size() ; i++)
			{
				temp = hits[i];
				std::cout << "|" << temp.x << " " << temp.y << " | ";
			}
			std::cout << std::endl;*/
			hits.erase(hits.begin()+i);
			// debug print hits code
			/*
			for ( int i = 0; i < hits.size() ; i++)
			{
				temp = hits[i];
				std::cout << "|" << temp[0] << " " << temp[1] << " | ";
			}
			std::cout << std::endl;*/
		}
		//std::cout << "returning hit for player" << std::endl;
		return p;
	}
	else
	{
		//std::cout << "RUNNING CHECKERBOARD SEARCH" << std::endl;
		p = getRandomPatternHit();
		return p;
	}
	
}